#include <jni.h>

#include <stddef.h>

#define LOG_TAG "NVRomData"

#include <android/log.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>

#define ALOGI(fmt, args...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, fmt, ##args)
#define ALOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, fmt, ##args)
#define ALOGW(fmt, args...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, fmt, ##args)
#define ALOGE(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, fmt, ##args)

static const char* JAVA_CLASS_PATH_NAME = "com/android/server/roobo/RobotNVRomDataLocalJni";
static const char* JAVA_CLASS_PATH_NAME_INTEGER_INFO = "com/android/server/roobo/NVRomIntegerInfo";
static const char* JAVA_CLASS_PATH_NAME_STRING_INFO = "com/android/server/roobo/NVRomStringInfo";

/*
 * read me
 * 1. 我们使用 key-value 形式存储数据。key 是 String 格式。
 * 2. 我们使用 0 ~ 1M 空间来储存 value 是 int 型的数据。
 * 3. 我们使用 1M ~ 6M 空间来储存 value 是 String 型的数据。
 */
#define VNROM_DEV_PRIVDATA_PATH "/dev/block/platform/ff0f0000.dwmmc/by-name/privdata"
#define VNROM_KEY_INTEGER_ADDR_OFFSET 	0x0
#define VNROM_KEY_INTEGER_SIZE			(1 * 1024 * 1024)

#define VNROM_KEY_STRING_ADDR_OFFSET 	(VNROM_KEY_INTEGER_SIZE + VNROM_KEY_INTEGER_ADDR_OFFSET)
#define VNROM_KEY_STRING_SIZE			(5 * 1024 * 1024)

/////////////////////////////////////////////////////////////////////////////
//由于不清楚 VNROM 里面的默认值是什么。 所以用这个ID来标识 我们设置 了该 item
#define VNROM_ITEM_SET_ID		0xA5A5F11F
#define VNROM_KEY_SIZE 			63
#define VNROM_VALUE_STRING_SIZE 1023
struct item_integer {
	unsigned int set_id;
	char key[VNROM_KEY_SIZE + 1];//with '\0'
	int value;
	int offset_index;
	struct timeval set_time;

};

struct item_string {
	unsigned int set_id;
	char key[VNROM_KEY_SIZE + 1]; //with '\0'
	char value[VNROM_VALUE_STRING_SIZE + 1]; //with '\0'
	int offset_index;
	struct timeval set_time;
};


#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))

struct item_integer_cache {
	struct item_integer item;
	struct timeval rw_time;//记录最近读写的时间
};
struct item_string_cache {
	struct item_string item;
	struct timeval rw_time;//记录最近读写的时间
};
//当每个cache都有用到时，如果有新的的item进来， 会替换掉rw_time最晚那个。
struct item_integer_cache item_int_cache[10];
struct item_string_cache item_str_cache[5];


// return the readed length, <=0 -- error.
static int nvrom_read_priv_block_dev(void* buf, int size, int offset )
{
	int ret;
	const char* fp = VNROM_DEV_PRIVDATA_PATH;
	int fd = open(fp, O_RDONLY, 0);
	if(fd < 0){
		/* got log:
		 * open "/dev/block/platform/ff0f0000.dwmmc/by-name/privdata" fail.Permission denied.
		*/
		ALOGE("open %s for read fail.%s\n",fp, strerror(errno) );
		return fd;
	}

	memset(buf, 0, size);
	lseek(fd, offset, SEEK_SET);
	ret = read(fd, buf, size);
	if( ret <= 0 ){
		ALOGE("read %s fail.%s\n",fp, strerror(errno) );
		ret = -1;
	}
	close(fd);
    if( ret != size ){
		ALOGW("Read %s Error,size=%d,ret=%d" , fp, size, ret);
	}
	return ret;
}

// return the writed length, <=0 -- error.
static int nvrom_write_priv_block_dev(void* buf, int size, int offset )
{
	int ret;
	const char* fp = VNROM_DEV_PRIVDATA_PATH;
	int fd = open(fp, O_RDWR/*O_WRONLY*/, 0); //O_RDWR
	if(fd < 0){
		// open "/dev/block/platform/ff0f0000.dwmmc/by-name/privdata" fail.Permission denied
		ALOGE("open %s for write fail.%s\n",fp, strerror(errno) );
		return fd;
	}
	lseek(fd, offset, SEEK_SET);
	ret = write(fd, buf, size);

	if( ret <= 0 ){
			ALOGE("write %s fail.%s\n",fp, strerror(errno) );
		ret = -1;
	}
	close(fd);
    if( size != ret ){
        ALOGW("write %s error.size=%d,ret=%d\n",fp, size, ret);
    }
    return ret;
}

static int nvrom_save_item_integer(struct item_integer * w_item)
{
	const int item_all_count = VNROM_KEY_INTEGER_SIZE / sizeof(struct item_integer);
	struct item_integer * item_array;
	struct item_integer * item;
	int i;
	int ret;
	if (w_item->offset_index >= 0) {
		return nvrom_write_priv_block_dev(w_item, sizeof(struct item_integer),
								w_item->offset_index * sizeof(struct item_integer) + VNROM_KEY_INTEGER_ADDR_OFFSET);
	}

	ALOGE("nvrom_save_item_integer, sizeof(struct item_integer) = %zu", sizeof(struct item_integer));
	//it is new. find a slot for it.
	//it will loop through--bad performance
	item_array = (struct item_integer *) calloc(item_all_count, sizeof(struct item_integer));
	ret = nvrom_read_priv_block_dev((void *) item_array,
					item_all_count * sizeof(struct item_integer),
					VNROM_KEY_INTEGER_ADDR_OFFSET);

	if (ret != item_all_count * sizeof(struct item_integer)) {
		ALOGW("read priv block error. ret=%d\n", ret);
		free(item_array);
		return -1;
	}
	for(i = 0; i < item_all_count; i++) {
		item = item_array + i;
		if (item->set_id != VNROM_ITEM_SET_ID) {
			continue;
		}
		if (!strcmp(item->key, w_item->key)) {
			item->offset_index = i;
			break;
		}
	}
	if (i == item_all_count) {
		//not found;
		for(i = 0; i < item_all_count; i++) {
			item = item_array + i;
			if (item->set_id != VNROM_ITEM_SET_ID) {
				item->offset_index = i;
				break;
			}
		}
	} else {
		//found, check if the value is equal
		if (item->value == w_item->value) {
			free(item_array);
			ALOGW("save integer(%d) is the same as the the one on NVRom", w_item->value);
			return sizeof(*w_item);
		}
	}

	if (!item) {
		ALOGW("item is full. item_all_count(%d) is small.\n", item_all_count);
		free(item_array);
		return -1;
	}


	w_item->set_id = VNROM_ITEM_SET_ID;
	w_item->offset_index = item->offset_index;
	gettimeofday(&w_item->set_time, NULL);//time to rw the item

	free(item_array);

	ALOGW("nvrom_write_priv_block_dev offset_index = %d.\n", w_item->offset_index);

	return nvrom_write_priv_block_dev(w_item, sizeof(struct item_integer),
								w_item->offset_index * sizeof(struct item_integer) + VNROM_KEY_INTEGER_ADDR_OFFSET);

}


static int nvrom_save_item_string(struct item_string * w_item)
{
	const int item_all_count = VNROM_KEY_STRING_SIZE / sizeof(struct item_string);
	struct item_string * item_array;
	struct item_string * item;
	int i;
	int ret;
	if (w_item->offset_index >= 0) {
		return nvrom_write_priv_block_dev(w_item, sizeof(struct item_string),
								w_item->offset_index * sizeof(struct item_string) + VNROM_KEY_STRING_ADDR_OFFSET);
	}

	//it is new. find a slot for it.
	//it will loop through--bad
	item_array = (struct item_string * )calloc(item_all_count, sizeof(struct item_string));
	ret = nvrom_read_priv_block_dev((void *) item_array,
						item_all_count * sizeof(struct item_string),
						VNROM_KEY_STRING_ADDR_OFFSET);
	if (ret != item_all_count * sizeof(struct item_string)) {
		ALOGW("read priv block error. ret=%d\n", ret);
		free(item_array);
		return -1;
	}
	for(i = 0; i < item_all_count; i++) {
		item = item_array + i;
		if (item->set_id != VNROM_ITEM_SET_ID) {
			continue;
		}
		if (!strcmp(item->key, w_item->key)) {
			item->offset_index = i;
			break;
		}
	}
	if (i == item_all_count) {
		//not found;
		for(i = 0; i < item_all_count; i++) {
			item = item_array + i;
			if (item->set_id != VNROM_ITEM_SET_ID) {
				item->offset_index = i;
				break;
			}
		}
	} else {

		//found, check if the value is equal
		if (!strcmp(item->value, w_item->value)) {
			free(item_array);
			ALOGW("save string(%s) is the same as the the one on NVRom", w_item->value);
			return sizeof(*w_item);
		}

	}

	if (!item) {
		ALOGW("item is full. item_all_count(%d) is small.\n", item_all_count);
		free(item_array);
		return -1;
	}

	w_item->set_id = VNROM_ITEM_SET_ID;
	w_item->offset_index = item->offset_index;
	gettimeofday(&w_item->set_time, NULL);//time to rw the item

	free(item_array);

	return nvrom_write_priv_block_dev(w_item, sizeof(struct item_string),
								w_item->offset_index * sizeof(struct item_string) + VNROM_KEY_STRING_ADDR_OFFSET);

}


static struct tm * toGMTTime(struct timeval time)
{
	return gmtime(&time.tv_sec);//转为格林威治时间
}

static void printTime(struct timeval time)
{
	struct tm * gmt = toGMTTime(time);

	ALOGE("%04d-%02d-%02d %02d:%02d:%02d",
		gmt->tm_year + 1900, gmt->tm_mon + 1, gmt->tm_mday,
		gmt->tm_hour, gmt->tm_min, gmt->tm_sec);
}

static struct item_integer_cache * find_item_in_integer_cache(const char * key, bool * _mine)
{
	struct item_integer_cache * cache = NULL;
	struct item_integer_cache * cache_temp = NULL;
	int i = 0;
	bool mine = false;

	//find key name first, if not found , find set_id
	for(i = 0; i < ARRAY_SIZE(item_int_cache); i++) {
		cache_temp = &item_int_cache[i];
		if (!strcmp(cache_temp->item.key, key)) {
			cache = cache_temp;
			mine = true;
			break;
		}
	}

	if (!cache) {
		//try find set id,
		for(i = 0; i < ARRAY_SIZE(item_int_cache); i++) {
			cache_temp = &item_int_cache[i];
			if (cache_temp->item.set_id != VNROM_ITEM_SET_ID) {
				cache = cache_temp;
				cache->item.offset_index = -1;
				break;
			}
		}
	}


	if (!cache) {
		//no cache found. replace the one cache depend on rw_time
		cache = &item_int_cache[0];
		for(i = 1; i < ARRAY_SIZE(item_int_cache); i++) {
			cache_temp = &item_int_cache[i];
			if (cache->rw_time.tv_sec > cache_temp->rw_time.tv_sec) {
				cache = cache_temp;
			} else if (cache->rw_time.tv_usec > cache_temp->rw_time.tv_usec) {
				cache = cache_temp;
			}
		}
		ALOGW("integer -> replace cache key = %s. offset_index = %d", cache->item.key, cache->item.offset_index );
		cache->item.offset_index = -1;
	}

	*_mine = mine;
	return cache;

}

static bool setKeyValueInteger(const char * key, int value)
{
	int ret;
	bool mine = false;
	struct item_integer_cache * cache = find_item_in_integer_cache(key, &mine);

	assert(cache);

	if (cache->item.set_id == VNROM_ITEM_SET_ID) {
		struct tm * gmt = toGMTTime(cache->rw_time);
		ALOGW("cache key = %s. offset_index = %d, set time: %04d-%02d-%02d %02d:%02d:%02d",
		cache->item.key, cache->item.offset_index,
		gmt->tm_year + 1900, gmt->tm_mon + 1, gmt->tm_mday,
		gmt->tm_hour, gmt->tm_min, gmt->tm_sec);
	}

	cache->item.set_id = VNROM_ITEM_SET_ID;
	gettimeofday(&cache->rw_time, NULL);//time to rw the item

	if (!mine) {

		strcpy(cache->item.key, key);

	} else if (cache->item.value == value) {
		ALOGW("set value(%d) is same as the one in cache", value);
		return true;
	}

	//start set key-value
	ALOGW("set key-value = %s: %d", key, value);
	cache->item.value = value;
	gettimeofday(&cache->item.set_time, NULL);

	ret = nvrom_save_item_integer(&cache->item);
	if (ret != sizeof(struct item_integer)) {
		cache->item.value = 0;
		cache->item.set_id = 0;
		memset(&cache->item.key, 0, sizeof(cache->item.key));

		ALOGW("Failed to set key-value = %s:0x%x", key, value);

		return false;
	}

	return true;
}

static int getKeyValueInteger(const char * key, int default_value)
{
	struct item_integer * item_array = NULL;
	struct item_integer * item = NULL;
	const int item_all_count = VNROM_KEY_INTEGER_SIZE / sizeof(struct item_integer);

	struct item_integer_cache * cache = NULL;
	struct item_integer_cache * cache_temp = NULL;
	int i = 0;
	int ret;

	for(i = 0; i < ARRAY_SIZE(item_int_cache); i++) {
		cache_temp = &item_int_cache[i];
		if (!strcmp(cache_temp->item.key, key)) {
			cache = cache_temp;
			break;
		}
	}

	if (cache) {
		ALOGE("cache->item.key = %s, cache->item.offset_index = %d.", cache->item.key, cache->item.offset_index);
		return cache->item.value;
	}


	//it is new. find a slot for it.
	//it will loop through--bad
	item_array = (struct item_integer *) calloc(item_all_count, sizeof(struct item_integer));
	ret = nvrom_read_priv_block_dev((void *) item_array,
						item_all_count * sizeof(struct item_integer),
						VNROM_KEY_INTEGER_ADDR_OFFSET);
	if (ret != item_all_count * sizeof(struct item_integer)) {
		ALOGW("read priv block error. ret=%d\n", ret);
		free(item_array);
		return default_value;
	}
	for(i = 0; i < item_all_count; i++) {
		item = item_array + i;
		if (item->set_id != VNROM_ITEM_SET_ID) {
			continue;
		}
		if (!strcmp(item->key, key)) {
			break;
		}
	}
	if (i == item_all_count || !item) {
		//not found;
		free(item_array);
		return default_value;

	}

	ALOGE("interger->read item.key = %s, item.offset_index = %d.", item->key, item->offset_index);

	//get a cache
	for(i = 0; i < ARRAY_SIZE(item_int_cache); i++) {
		cache_temp = &item_int_cache[i];
		if (cache_temp->item.set_id != VNROM_ITEM_SET_ID) {
			cache = cache_temp;
			break;
		}
	}
	if (!cache) {
		//no free cache found
		cache = &item_int_cache[0];
		for(i = 1; i < ARRAY_SIZE(item_int_cache); i++) {
			cache_temp = &item_int_cache[i];
			if (cache->rw_time.tv_sec > cache_temp->rw_time.tv_sec) {
				cache = cache_temp;
			} else if (cache->rw_time.tv_usec > cache_temp->rw_time.tv_usec) {
				cache = cache_temp;
			}
		}

		ALOGE("getKeyValueInteger->replaced cache->item.key = %s, item origin offset_index = %d.", cache->item.key, cache->item.offset_index);
		cache->item.offset_index = -1;

	}

	if (cache) {
		memcpy(&cache->item, item, sizeof(*item));
		gettimeofday(&cache->rw_time, NULL);
		ret = cache->item.value;
	}

	free(item_array);

	return ret;

}


static bool removeKeyValueInteger(const char * key)
{
	struct item_integer * item_array = NULL;
	struct item_integer * item = NULL;
	const int item_all_count = VNROM_KEY_INTEGER_SIZE / sizeof(struct item_integer);

	struct item_integer_cache * cache = NULL;
	struct item_integer_cache * cache_temp = NULL;
	int i = 0;
	int ret;

	for(i = 0; i < ARRAY_SIZE(item_int_cache); i++) {
		cache_temp = &item_int_cache[i];
		if (!strcmp(cache_temp->item.key, key)) {
			cache = cache_temp;
			break;
		}
	}

	if (cache) {
		memset(cache, 0, sizeof(*cache));
	}


	//it is new. find a slot for it.
	//it will loop through--bad
	item_array = (struct item_integer *) calloc(item_all_count, sizeof(struct item_integer));
	ret = nvrom_read_priv_block_dev((void *) item_array,
						item_all_count * sizeof(struct item_integer),
						VNROM_KEY_INTEGER_ADDR_OFFSET);
	if (ret != item_all_count * sizeof(struct item_integer)) {
		ALOGW("read priv block error. ret=%d\n", ret);
		free(item_array);
		return false;
	}
	for(i = 0; i < item_all_count; i++) {
		item = item_array + i;
		if (item->set_id != VNROM_ITEM_SET_ID) {
			continue;
		}
		if (!strcmp(item->key, key)) {
			break;
		}
	}
	if (i == item_all_count || !item) {
		//not found;
		free(item_array);
		return true;

	}

	ALOGE("remove: read item.key = %s, item.offset_index = %d.", item->key, item->offset_index);

	memset(item->key, 0, sizeof(item->key));
	item->set_id = 0;
	memset(&item->set_time, 0, sizeof(item->set_time));

	ret = (sizeof(*item) == nvrom_save_item_integer(item));

	free(item_array);

	return ret;

}


static jobjectArray getAllInteger(JNIEnv* env, jclass clazz)
{

	struct item_integer * item_array = NULL;
	struct item_integer * item = NULL;
	const int item_all_count = VNROM_KEY_INTEGER_SIZE / sizeof(struct item_integer);
	int valid_count = 0;
	int ret;
	int array_index = 0;

	jclass infoClass = env->FindClass(JAVA_CLASS_PATH_NAME_INTEGER_INFO);
	jobjectArray myArray = NULL;

	item_array = (struct item_integer *) calloc(item_all_count, sizeof(struct item_integer));
	ret = nvrom_read_priv_block_dev((void *) item_array,
						item_all_count * sizeof(struct item_integer),
						VNROM_KEY_INTEGER_ADDR_OFFSET);
	if (ret != item_all_count * sizeof(struct item_integer)) {
		ALOGW("read priv block error. ret=%d\n", ret);
		free(item_array);
		return NULL;
	}

	for(int i = 0; i < item_all_count; i ++) {
		item = item_array + i;
		if (item->set_id == VNROM_ITEM_SET_ID && strlen(item->key) > 0) {
			valid_count++;
		}
	}

	myArray =  (jobjectArray)env->NewObjectArray(valid_count, infoClass, NULL);

	array_index = 0;
	for(int i = 0; i < item_all_count; i ++) {
		item = item_array + i;

		if (!(item->set_id == VNROM_ITEM_SET_ID && strlen(item->key) > 0)) {
			continue;
		}

		jmethodID init = env->GetMethodID(infoClass, "<init>","()V");
		jobject jInfo = env->NewObject(infoClass, init);

		env->SetObjectArrayElement(myArray, array_index++, jInfo);

		jfieldID  jID_key = env->GetFieldID(infoClass, "key", "Ljava/lang/String;");
		jfieldID  jID_value = env->GetFieldID(infoClass, "value", "I");
		jfieldID  jID_offset_index = env->GetFieldID(infoClass, "offset_index", "I");
		jfieldID  jID_time_s = env->GetFieldID(infoClass, "set_time_s", "J");
		jfieldID  jID_time_us = env->GetFieldID(infoClass, "set_time_us", "J");

		jstring jstr = (jstring)(env->NewStringUTF(item->key));
		env->SetObjectField(jInfo, jID_key, jstr);
		env->SetIntField(jInfo, jID_value, item->value);
		env->SetIntField(jInfo, jID_offset_index, item->offset_index);
		env->SetLongField(jInfo, jID_time_s, (jlong)(item->set_time.tv_sec));
		env->SetLongField(jInfo, jID_time_us, (jlong)(item->set_time.tv_usec));
	}

	free(item_array);
	return myArray;
}


static struct item_string_cache * find_item_in_string_cache(const char * key, bool * _mine)
{
	struct item_string_cache * cache = NULL;
	struct item_string_cache * cache_temp = NULL;
	int i = 0;
	bool mine = false;

	//find key name first, if not found , find set_id
	for(i = 0; i < ARRAY_SIZE(item_str_cache); i++) {
		cache_temp = &item_str_cache[i];
		if (!strcmp(cache_temp->item.key, key)) {
			cache = cache_temp;
			mine = true;
			break;
		}
	}

	if (!cache) {
		//try find set id,
		for(i = 0; i < ARRAY_SIZE(item_str_cache); i++) {
			cache_temp = &item_str_cache[i];
			if (cache_temp->item.set_id != VNROM_ITEM_SET_ID) {
				cache = cache_temp;
				cache->item.offset_index = -1;
				break;
			}
		}
	}

	if (!cache) {
		//no cache found. replace the one cache depend on rw_time
		cache = &item_str_cache[0];
		for(i = 1; i < ARRAY_SIZE(item_str_cache); i++) {
			cache_temp = &item_str_cache[i];
			if (cache->rw_time.tv_sec > cache_temp->rw_time.tv_sec) {
				cache = cache_temp;
			} else if (cache->rw_time.tv_usec > cache_temp->rw_time.tv_usec) {
				cache = cache_temp;
			}
		}

		ALOGW("string-> replace cache key = %s. offset_index = %d", cache->item.key, cache->item.offset_index );
		cache->item.offset_index = -1;
	}

	*_mine = mine;
	return cache;

}


static bool setKeyValueString(const char * key, const char * value)
{
	int ret;
	bool mine = false;

	struct item_string_cache * cache = find_item_in_string_cache(key, &mine);

	assert(cache);

	if (cache->item.set_id == VNROM_ITEM_SET_ID) {
		struct tm * gmt = toGMTTime(cache->rw_time);
		ALOGW("cache key = %s. offset_index = %d, set time: %04d-%02d-%02d %02d:%02d:%02d",
		cache->item.key, cache->item.offset_index,
		gmt->tm_year + 1900, gmt->tm_mon + 1, gmt->tm_mday,
		gmt->tm_hour, gmt->tm_min, gmt->tm_sec);
	}

	cache->item.set_id = VNROM_ITEM_SET_ID;
	gettimeofday(&cache->rw_time, NULL);//time to rw the item

	if (!mine) {

		strcpy(cache->item.key, key);

	} else if (!strcmp(cache->item.value, value)) {

		ALOGW("set value(%s) is same as the one in cache", value);
		return true;
	}

	//start set key-value
	ALOGW("set key-value = %s : %s", key, value);
	strcpy(cache->item.value, value);

	gettimeofday(&cache->item.set_time, NULL);

	ret = nvrom_save_item_string(&cache->item);
	if (ret != sizeof(struct item_string)) {

		cache->item.set_id = 0;
		memset(&cache->item.key, 0, sizeof(cache->item.key));
		memset(&cache->item.value, 0, sizeof(cache->item.value));

		ALOGW("Failed to set key-value = %s:%s", key, value);

		return false;
	}

	return true;

}


static bool getKeyValueString(const char * key, char ** str)
{
	struct item_string * item_array = NULL;
	struct item_string * item = NULL;
	const int item_all_count = VNROM_KEY_STRING_SIZE / sizeof(struct item_string);

	struct item_string_cache * cache = NULL;
	struct item_string_cache * cache_temp = NULL;
	int i = 0;
	int ret;


	for(i = 0; i < ARRAY_SIZE(item_str_cache); i++) {
		cache_temp = &item_str_cache[i];
		if (!strcmp(cache_temp->item.key, key)) {
			cache = cache_temp;
			break;
		}
	}

	if (cache) {
		ALOGE("cache->item.key = %s, cache->item.offset_index = %d.", cache->item.key, cache->item.offset_index);
		*str = cache->item.value;
		return true;
	}

	//it is new. find a slot for it.
	//it will loop through--bad
	item_array = (struct item_string *)calloc(item_all_count, sizeof(struct item_string));
	ret = nvrom_read_priv_block_dev((void *) item_array,
						item_all_count * sizeof(struct item_string),
						VNROM_KEY_STRING_ADDR_OFFSET);
	if (ret != item_all_count * sizeof(struct item_string)) {
		ALOGW("read priv block error. ret=%d\n", ret);
		free(item_array);
		return false;
	}
	for(i = 0; i < item_all_count; i++) {
		item = item_array + i;
		if (item->set_id != VNROM_ITEM_SET_ID) {
			continue;
		}
		if (!strcmp(item->key, key)) {
			break;
		}
	}
	if (i == item_all_count || !item) {
		//not found;

		return false;

	}

	ALOGE("string->read item.key = %s, item.offset_index = %d.", item->key, item->offset_index);

	//get a cache
	for(i = 0; i < ARRAY_SIZE(item_str_cache); i++) {
		cache_temp = &item_str_cache[i];
		if (cache_temp->item.set_id != VNROM_ITEM_SET_ID) {
			cache = cache_temp;
			break;
		}
	}
	if (!cache) {
		//no free cache found
		cache = &item_str_cache[0];
		for(i = 1; i < ARRAY_SIZE(item_str_cache); i++) {
			cache_temp = &item_str_cache[i];
			if (cache->rw_time.tv_sec > cache_temp->rw_time.tv_sec) {
				cache = cache_temp;
			} else if (cache->rw_time.tv_usec > cache_temp->rw_time.tv_usec) {
				cache = cache_temp;
			}
		}

		ALOGE("getKeyValueString-> replaced cache->item.key = %s, item origin offset_index = %d.", cache->item.key, cache->item.offset_index);
		cache->item.offset_index = -1;

	}

	if (cache) {
		memcpy(&cache->item, item, sizeof(*item));
		gettimeofday(&cache->rw_time, NULL);
		*str = cache->item.value;
	}

	free(item_array);

	return true;
}


static bool removeKeyValueString(const char * key)
{
	struct item_string * item_array = NULL;
	struct item_string * item = NULL;
	const int item_all_count = VNROM_KEY_STRING_SIZE / sizeof(struct item_string);

	struct item_string_cache * cache = NULL;
	struct item_string_cache * cache_temp = NULL;
	int i = 0;
	int ret;

	for(i = 0; i < ARRAY_SIZE(item_str_cache); i++) {
		cache_temp = &item_str_cache[i];
		if (!strcmp(cache_temp->item.key, key)) {
			cache = cache_temp;
			break;
		}
	}

	if (cache) {
		memset(cache, 0, sizeof(*cache));
	}

	//it is new. find a slot for it.
	//it will loop through--bad
	item_array = (struct item_string *)calloc(item_all_count, sizeof(struct item_string));
	ret = nvrom_read_priv_block_dev((void *) item_array,
						item_all_count * sizeof(struct item_string),
						VNROM_KEY_STRING_ADDR_OFFSET);
	if (ret != item_all_count * sizeof(struct item_string)) {
		ALOGW("read priv block error. ret=%d\n", ret);
		free(item_array);
		return false;
	}
	for(i = 0; i < item_all_count; i++) {
		item = item_array + i;
		if (item->set_id != VNROM_ITEM_SET_ID) {
			continue;
		}
		if (!strcmp(item->key, key)) {
			break;
		}
	}
	if (i == item_all_count || !item) {
		//not found;
		return true;

	}

	ALOGE("remove: read item.key = %s, item.offset_index = %d.", item->key, item->offset_index);

	memset(item->key, 0, sizeof(item->key));
	memset(item->value, 0, sizeof(item->value));
	item->set_id = 0;
	memset(&item->set_time, 0, sizeof(item->set_time));

	ret = (sizeof(*item) == nvrom_save_item_string(item));

	free(item_array);

	return true;
}


static jobjectArray getAllString(JNIEnv* env, jclass clazz)
{

	struct item_string * item_array = NULL;
	struct item_string * item = NULL;
	const int item_all_count = VNROM_KEY_STRING_SIZE / sizeof(struct item_string);
	int valid_count = 0;
	int ret;
	int array_index = 0;

	jclass infoClass = env->FindClass(JAVA_CLASS_PATH_NAME_STRING_INFO);
	jobjectArray myArray = NULL;

	item_array = (struct item_string *)calloc(item_all_count, sizeof(struct item_string));
	ret = nvrom_read_priv_block_dev((void *) item_array,
						item_all_count * sizeof(struct item_string),
						VNROM_KEY_STRING_ADDR_OFFSET);
	if (ret != item_all_count * sizeof(struct item_string)) {
		ALOGW("read priv block error. ret=%d\n", ret);
		free(item_array);
		return NULL;
	}

	for(int i = 0; i < item_all_count; i ++) {
		item = item_array + i;
		if (item->set_id == VNROM_ITEM_SET_ID && strlen(item->key) > 0) {
			valid_count++;
		}
	}

	myArray =  (jobjectArray)env->NewObjectArray(valid_count, infoClass, NULL);

	array_index = 0;
	for(int i = 0; i < item_all_count; i ++) {
		item = item_array + i;

		if (!(item->set_id == VNROM_ITEM_SET_ID && strlen(item->key) > 0)) {
			continue;
		}

		jmethodID init = env->GetMethodID(infoClass, "<init>","()V");
		jobject jInfo = env->NewObject(infoClass, init);

		env->SetObjectArrayElement(myArray, array_index++, jInfo);

		jfieldID  jID_key = env->GetFieldID(infoClass, "key", "Ljava/lang/String;");
		jfieldID  jID_value = env->GetFieldID(infoClass, "value", "Ljava/lang/String;");
		jfieldID  jID_offset_index = env->GetFieldID(infoClass, "offset_index", "I");
		jfieldID  jID_time_s = env->GetFieldID(infoClass, "set_time_s", "J");
		jfieldID  jID_time_us = env->GetFieldID(infoClass, "set_time_us", "J");

		jstring jstrKey = (jstring)(env->NewStringUTF(item->key));
		env->SetObjectField(jInfo, jID_key, jstrKey);
		jstring jstrValue = (jstring)(env->NewStringUTF(item->value));
		env->SetObjectField(jInfo, jID_value, jstrValue);
		env->SetIntField(jInfo, jID_offset_index, item->offset_index);
		env->SetLongField(jInfo, jID_time_s, (jlong)(item->set_time.tv_sec));
		env->SetLongField(jInfo, jID_time_us, (jlong)(item->set_time.tv_usec));
	}

	free(item_array);
	return myArray;
}

/////////////////////////////////////////////////////////////////////////////

static jboolean setIntegerNVRom(JNIEnv* env, jclass clazz,
	jstring key, int value ) {
	bool result = false;
	char *c_key = (char *) (env)->GetStringUTFChars(key, NULL);
	if(c_key){
		if (strlen(c_key) > VNROM_KEY_SIZE) {
			c_key[VNROM_KEY_SIZE] = '\0';
			ALOGE("WARNING: key size(%zu) is longer than %d. valid key is %s.", strlen(c_key), VNROM_KEY_SIZE, c_key);
		}
		result = setKeyValueInteger((const char *)c_key, value);
		(env)->ReleaseStringUTFChars(key, c_key );
	}

	return (jboolean)result;
}

static jint getIntegerNVRom(JNIEnv* env, jclass clazz,
	jstring key , jint defaultData ) {

	int result = defaultData;
	char *c_key = (char *) (env)->GetStringUTFChars(key, NULL);
	if(c_key){
		if (strlen(c_key) > VNROM_KEY_SIZE) {
			c_key[VNROM_KEY_SIZE] = '\0';
			ALOGE("WARNING: key size(%zu) is longer than %d. valid key is %s.", strlen(c_key), VNROM_KEY_SIZE, c_key);
		}
		result = getKeyValueInteger((const char *)c_key, result);
		(env)->ReleaseStringUTFChars(key, c_key );
	}

	return result;
}

static jboolean removeIntegerNVRom(JNIEnv* env, jclass clazz, jstring key ) {

	bool result = false;
	char *c_key = (char *) (env)->GetStringUTFChars(key, NULL);
	if(c_key){
		if (strlen(c_key) > VNROM_KEY_SIZE) {
			c_key[VNROM_KEY_SIZE] = '\0';
			ALOGE("WARNING: key size(%zu) is longer than %d. valid key is %s.", strlen(c_key), VNROM_KEY_SIZE, c_key);
		}
		result = removeKeyValueInteger((const char *)c_key);
		(env)->ReleaseStringUTFChars(key, c_key );
	}

	return (jboolean)result;
}

static jobjectArray getAllIntegerNVRom(JNIEnv* env, jclass clazz)
{
	return getAllInteger(env, clazz);
}


static jboolean setStringNVRom(JNIEnv* env, jclass clazz,
	jstring key, jstring content) {

	bool result = false;
	char *c_key = (char *) (env)->GetStringUTFChars(key, NULL);
	char *the_content = (char *) (env)->GetStringUTFChars(content,NULL);

	if(c_key && the_content){

		if (strlen(c_key) > VNROM_KEY_SIZE) {
			c_key[VNROM_KEY_SIZE] = '\0';
			ALOGE("WARNING: key size(%zu) is longer than %d. valid key is %s.", strlen(c_key), VNROM_KEY_SIZE, c_key);
		}
		if (strlen(the_content) > VNROM_VALUE_STRING_SIZE) {
			c_key[VNROM_VALUE_STRING_SIZE] = '\0';
			ALOGE("WARNING: key size(%zu) is longer than %d. valid key is %s.", strlen(the_content), VNROM_VALUE_STRING_SIZE, the_content);
		}
		result = setKeyValueString((const char *)c_key, (const char *)the_content);

		(env)->ReleaseStringUTFChars(key, c_key );
		(env)->ReleaseStringUTFChars( content, the_content );
	}

	return (jboolean)result;
}

static jstring getStringNVRom(JNIEnv* env, jclass clazz,
	jstring key) {

	char * str;
	bool ret = false;
	char *c_key = (char *) (env)->GetStringUTFChars(key, NULL);
	if(c_key){
		if (strlen(c_key) > VNROM_KEY_SIZE) {
			c_key[VNROM_KEY_SIZE] = '\0';
			ALOGE("WARNING: key size(%zu) is longer than %d. valid key is %s.", strlen(c_key), VNROM_KEY_SIZE, c_key);
		}
		ret = getKeyValueString((const char *)c_key, &str);
		(env)->ReleaseStringUTFChars(key, c_key );
	}
	if (!ret) {
		return env->NewStringUTF("");
	}
	return env->NewStringUTF(str);
}

static jboolean removeStringNVRom(JNIEnv* env, jclass clazz,
		jstring key) {

	bool result = false;
	char *c_key = (char *) (env)->GetStringUTFChars(key, NULL);
	if(c_key){
		if (strlen(c_key) > VNROM_KEY_SIZE) {
			c_key[VNROM_KEY_SIZE] = '\0';
			ALOGE("WARNING: key size(%zu) is longer than %d. valid key is %s.", strlen(c_key), VNROM_KEY_SIZE, c_key);
		}
		result = removeKeyValueString((const char *)c_key);
		(env)->ReleaseStringUTFChars(key, c_key );
	}

	return (jboolean) result;
}

static jobjectArray getAllStringNVRom(JNIEnv* env, jclass clazz)
{
	return getAllString(env, clazz);
}


static JNINativeMethod method_table[] = {

	// key - integer
	{ "setIntegerNVRom", "(Ljava/lang/String;I)Z", (void*)setIntegerNVRom },
	{ "getIntegerNVRom", "(Ljava/lang/String;I)I", (void*)getIntegerNVRom },
	{ "removeIntegerNVRom", "(Ljava/lang/String;)Z", (void*)removeIntegerNVRom },
	{ "getAllIntegerNVRom", "()[Lcom/android/server/roobo/NVRomIntegerInfo;", (void*)getAllIntegerNVRom },

	//key - string
	{ "getStringNVRom", "(Ljava/lang/String;)Ljava/lang/String;", (void*) getStringNVRom },
	{ "setStringNVRom", "(Ljava/lang/String;Ljava/lang/String;)Z", (void*) setStringNVRom },
	{ "removeStringNVRom", "(Ljava/lang/String;)Z", (void*)removeStringNVRom },

	{ "getAllStringNVRom", "()[Lcom/android/server/roobo/NVRomStringInfo;", (void*)getAllStringNVRom },
};

//此函数通过调用JNI中 RegisterNatives 方法来注册我们的函数
static int registerNativeMethods(JNIEnv* env, const char* className,JNINativeMethod* getMethods,int methodsNum){
    jclass clazz;
    //找到声明native方法的类
    clazz = env->FindClass(className);
    if(clazz == NULL){
        return JNI_FALSE;
    }
   //注册函数 参数：java类 所要注册的函数数组 注册函数的个数
    if(env->RegisterNatives(clazz, getMethods, methodsNum) < 0){
        return JNI_FALSE;
    }
    return JNI_TRUE;
}
static int registerNatives(JNIEnv* env) {
	//指定类的路径，通过FindClass 方法来找到对应的类
	const char* className = JAVA_CLASS_PATH_NAME;
	return registerNativeMethods(env, className, method_table,
			sizeof(method_table) / sizeof(method_table[0]));
}
//回调函数 在这里面注册函数
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved){
    JNIEnv* env = NULL;
   //判断虚拟机状态是否有问题
    if(vm->GetEnv((void**)&env,JNI_VERSION_1_6)!= JNI_OK){
        return -1;
    }
    assert(env != NULL);
    //开始注册函数 registerNatives -》registerNativeMethods -》env->RegisterNatives
    if(!registerNatives(env)){
        return -1;
    }
    //返回jni 的版本
    return JNI_VERSION_1_6;
}
